#region Using directives

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using Weazel.Math;

#endregion

namespace Weazel.Gezel.Simulator.Reference
{
    [IpBlockAttribute("byte_memory")]
    class ByteMemoryBlock : AbstractIpBlock
    {
        private string RamRead = "ram_rd";
        private string RamWrite = "ram_wr";
        private string RamAddress = "ram_addr";
        private string RamData = "ram_data";
        private string DeviceData = "device_data";

        private int RamReadIndex = -1;
        private int RamWriteIndex = -1;
        private int RamAddressIndex = -1;
        private int RamDataIndex = -1;
        private int DeviceDataIndex = -1;

        byte[] content;
        int size;
        private string filename;

        public ByteMemoryBlock(string name)
            : base(name)
        {
        }

        public override bool CannotSleepTest()
        {
            return false; // ram can always cause 'sleep' mode
        }

        public override void Run()
        {
            uint address = ioValues[RamAddressIndex].ToUInt();

            if (ioValues[RamReadIndex].ToInt() == 1)
            {
                if (address >= content.Length)
                {
                    throw new InvalidAddressException(address);
                }

                uint value = (uint)(content[address + 0]);
                ioValues[DeviceDataIndex].Assign(value);
            }
            else if (ioValues[RamWriteIndex].ToInt() == 1)
            {
                if (address >= content.Length)
                {
                    throw new InvalidAddressException(address);
                }

                uint value = ioValues[RamDataIndex].ToUInt();
                content[address + 0] = (byte)(value);                
            }
        }

        public override void SetParameter(string name)
        {
            string field = name.Substring(0, name.IndexOf('='));
            string value = name.Substring(name.IndexOf('=') + 1);

            switch (field)
            {
                case "file":
                    {
                        this.filename = value;

                        Stream stream = null;
                        try
                        {
                            stream = File.Open(filename, FileMode.Open);

                            if (content == null)
                            {
                                size = (int)stream.Length;
                                content = new byte[size];
                            }

                            int i = 0;
                            int c;

                            while (i < size && (c = stream.ReadByte()) != -1)
                                content[i++] = (byte)c;
                        }
                        catch (Exception e)
                        {
                            throw e;
                        }
                        finally
                        {
                            if (stream != null)
                                stream.Close();
                        }
                        break;
                    }
                case "size":
                    {
                        this.size = Convert.ToInt32(value, 16);

                        if (content != null)
                        {
                            // already set, probably by file parameter set code

                            byte[] newContent = new byte[size];

                            // copy
                            for (int i = 0; i < size; i++)
                            {
                                if (i < content.Length)
                                    newContent[i] = content[i];
                            }

                            content = newContent;
                        }
                        else
                        {
                            content = new byte[size];
                        }

                        break;
                    }
                default:
                    throw new InvalidOperationException("invalid parameter '" + field + "'");
            }
        }

        public override bool CheckTerminal(int n, string tname, AbstractIpBlock.IoDirection dir)
        {
            if (tname == RamRead && dir == IoDirection.Input)
                RamReadIndex = n;
            else if (tname == RamWrite && dir == IoDirection.Input)
                RamWriteIndex = n;
            else if (tname == RamAddress && dir == IoDirection.Input)
                RamAddressIndex = n;
            else if (tname == RamData && dir == IoDirection.Input)
                RamDataIndex = n;
            else if (tname == DeviceData && dir == IoDirection.Output)
                DeviceDataIndex = n;

            return true;
        }
    }
}
